package loggers

import (
	"context"
	"sync"

	fzap "github.com/mars/third_part/fork/zap"
	"github.com/mars/third_part/fork/zap/zcore"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

// Logger is interface which defines the standar of logger
type Logger interface {
	Debug(args ...interface{})
	Debugf(format string, args ...interface{})
	Info(args ...interface{})
	Infof(format string, args ...interface{})
	Warn(args ...interface{})
	Warnf(format string, args ...interface{})
	Error(args ...interface{})
	Errorf(format string, args ...interface{})
}

var (
	log  *logger = buildLogger(NewConfig()) // standar business logger
	once sync.Once
)

type logger struct {
	fieldKeys []string
	logger    *fzap.SugaredLogger
}

// Setup initilize the log instance
func Setup(config *Config) {
	once.Do(func() {
		log = buildLogger(config)
	})
}

func buildLogger(config *Config) *logger {
	zapcoreConfig := zapcore.EncoderConfig{
		LineEnding:  zapcore.DefaultLineEnding,
		EncodeLevel: zcore.UppercaseLevelEncoder,
		EncodeTime:  zapcore.TimeEncoderOfLayout(zcore.StandarTime),
	}
	encoder := zcore.NewPrettyEncoder(zapcoreConfig)
	core := zapcore.NewCore(encoder, zapcore.AddSync(config.parseWriteSyncer()), config.parseLevel())
	coreLogger := zap.New(core, zap.WithCaller(true), zap.AddCallerSkip(2))
	return &logger{
		fieldKeys: config.FieldKeys,
		logger:    fzap.Sugar(coreLogger),
	}
}

// Instance returns the logger object. Before use this func, please call Setup.
func Instance() *logger {
	return log
}

// Sync 日志刷新到文件
func Sync() {
	log.logger.Sync()
}

// pair get key-value pair from context.Context according to the keys specified by logger instance. please define own
// the ctx which should implement context.Context instead of the native context.Context. such as you can use gin.Context
// return []zap.Field
func (l *logger) pair(ctx context.Context) []zap.Field {
	var fields = make([]zap.Field, 0, len(l.fieldKeys))
	for _, key := range l.fieldKeys {
		value := ctx.Value(key)
		if value == nil {
			continue
		}
		fields = append(fields, zap.Any(key, value))
	}
	return fields
}

func (l *logger) RawLogger() *fzap.SugaredLogger {
	return l.logger
}

func (l *logger) Printf(format string, args ...interface{}) {
	l.logger.Log(zap.InfoLevel, format, args, nil)
}

func (l *logger) Debug(args ...interface{}) {
	l.logger.Log(zap.DebugLevel, "", args, nil)
}

func (l *logger) Debugf(format string, args ...interface{}) {
	l.logger.Log(zap.DebugLevel, format, args, nil)
}

func (l *logger) Info(args ...interface{}) {
	l.logger.Log(zap.InfoLevel, "", args, nil)
}

func (l *logger) Infof(format string, args ...interface{}) {
	l.logger.Log(zap.InfoLevel, format, args, nil)
}

func (l *logger) Warn(args ...interface{}) {
	l.logger.Log(zap.WarnLevel, "", args, nil)
}

func (l *logger) Warnf(format string, args ...interface{}) {
	l.logger.Log(zap.WarnLevel, format, args, nil)
}

func (l *logger) Error(args ...interface{}) {
	l.logger.Log(zap.ErrorLevel, "", args, nil)
}

func (l *logger) Errorf(format string, args ...interface{}) {
	l.logger.Log(zap.ErrorLevel, format, args, nil)
}

func Debug(args ...interface{}) {
	log.logger.Log(zap.DebugLevel, "", args, nil)
}

func Debugw(fields []zap.Field, args ...interface{}) {
	log.logger.Log(zap.DebugLevel, "", args, fields)
}

func Debugc(ctx context.Context, args ...interface{}) {
	log.logger.Log(zap.DebugLevel, "", args, log.pair(ctx))
}

func Debugf(format string, args ...interface{}) {
	log.logger.Log(zap.DebugLevel, format, args, nil)
}

func Debugfw(fields []zap.Field, format string, args ...interface{}) {
	log.logger.Log(zap.DebugLevel, format, args, fields)
}

func Debugfc(ctx context.Context, format string, args ...interface{}) {
	log.logger.Log(zap.DebugLevel, format, args, log.pair(ctx))
}

func Info(args ...interface{}) {
	log.logger.Log(zap.InfoLevel, "", args, nil)
}

func Infow(fields []zap.Field, args ...interface{}) {
	log.logger.Log(zap.InfoLevel, "", args, fields)
}

func Infoc(ctx context.Context, args ...interface{}) {
	log.logger.Log(zap.InfoLevel, "", args, log.pair(ctx))
}

func Infof(format string, args ...interface{}) {
	log.logger.Log(zap.InfoLevel, format, args, nil)
}

func Infofw(fields []zap.Field, format string, args ...interface{}) {
	log.logger.Log(zap.InfoLevel, format, args, fields)
}

func Infofc(ctx context.Context, format string, args ...interface{}) {
	log.logger.Log(zap.InfoLevel, format, args, log.pair(ctx))
}

func Warn(args ...interface{}) {
	log.logger.Log(zap.WarnLevel, "", args, nil)
}

func Warnw(fields []zap.Field, args ...interface{}) {
	log.logger.Log(zap.WarnLevel, "", args, fields)
}

func Warnc(ctx context.Context, args ...interface{}) {
	log.logger.Log(zap.WarnLevel, "", args, log.pair(ctx))
}

func Warnf(format string, args ...interface{}) {
	log.logger.Log(zap.WarnLevel, format, args, nil)
}

func Warnfw(fields []zap.Field, format string, args ...interface{}) {
	log.logger.Log(zap.WarnLevel, format, args, fields)
}

func Warnfc(ctx context.Context, format string, args ...interface{}) {
	log.logger.Log(zap.WarnLevel, format, args, log.pair(ctx))
}

func Error(args ...interface{}) {
	log.logger.Log(zap.ErrorLevel, "", args, nil)
}

func Errorw(fields []zap.Field, args ...interface{}) {
	log.logger.Log(zap.ErrorLevel, "", args, fields)
}

func Errorc(ctx context.Context, args ...interface{}) {
	log.logger.Log(zap.ErrorLevel, "", args, log.pair(ctx))
}

func Errorf(format string, args ...interface{}) {
	log.logger.Log(zap.ErrorLevel, format, args, nil)
}

func Errorfw(fields []zap.Field, format string, args ...interface{}) {
	log.logger.Log(zap.ErrorLevel, format, args, fields)
}

func Errorfc(ctx context.Context, format string, args ...interface{}) {
	log.logger.Log(zap.ErrorLevel, format, args, log.pair(ctx))
}

func Panic(args ...interface{}) {
	log.logger.Log(zap.PanicLevel, "", args, nil)
}

func Panicw(fields []zap.Field, args ...interface{}) {
	log.logger.Log(zap.PanicLevel, "", args, fields)
}

func Panicc(ctx context.Context, args ...interface{}) {
	log.logger.Log(zap.PanicLevel, "", args, log.pair(ctx))
}

func Panicf(format string, args ...interface{}) {
	log.logger.Log(zap.PanicLevel, format, args, nil)
}

func Panicfw(fields []zap.Field, format string, args ...interface{}) {
	log.logger.Log(zap.PanicLevel, format, args, fields)
}

func Panicfc(ctx context.Context, format string, args ...interface{}) {
	log.logger.Log(zap.PanicLevel, format, args, log.pair(ctx))
}

func Fatal(args ...interface{}) {
	log.logger.Log(zap.FatalLevel, "", args, nil)
}

func Fatalw(fields []zap.Field, args ...interface{}) {
	log.logger.Log(zap.FatalLevel, "", args, fields)
}

func Fatalc(ctx context.Context, args ...interface{}) {
	log.logger.Log(zap.FatalLevel, "", args, log.pair(ctx))
}

func Fatalf(format string, args ...interface{}) {
	log.logger.Log(zap.FatalLevel, format, args, nil)
}

func Fatalfw(fields []zap.Field, format string, args ...interface{}) {
	log.logger.Log(zap.FatalLevel, format, args, fields)
}

func Fatalfc(ctx context.Context, format string, args ...interface{}) {
	log.logger.Log(zap.FatalLevel, format, args, log.pair(ctx))
}

func Pair(ctx context.Context) []zap.Field {
	return log.pair(ctx)
}
